package com.bsj.sipgateway.sip.transmit.response.impl;

import com.alibaba.fastjson.JSON;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.def.entity.gb28181.Device;
import com.bsj.power.common.def.entity.gb28181.DeviceChannel;
import com.bsj.power.common.def.entity.log.CmdLog;
import com.bsj.power.common.def.enums.CmdLogEnum;
import com.bsj.power.common.mapper.CmdLogMapper;
import com.bsj.power.common.mapper.DeviceChannelMapper;
import com.bsj.power.common.mapper.DeviceMapper;
import com.bsj.sipgateway.command.RocketMqCommand;
import com.bsj.sipgateway.common.constant.Constants;
import com.bsj.sipgateway.config.SipConfig;
import com.bsj.sipgateway.mq.producer.CommandUpProducer;
import com.bsj.sipgateway.sip.SipLayer;
import com.bsj.sipgateway.sip.bean.Command;
import com.bsj.sipgateway.sip.session.CallIdManager;
import com.bsj.sipgateway.sip.session.CommandManager;
import com.bsj.sipgateway.sip.session.VideoStreamSessionManager;
import com.bsj.sipgateway.sip.transmit.cmd.impl.SIPCommander;
import com.bsj.sipgateway.sip.transmit.response.ISIPResponseProcessor;
import com.bsj.sipgateway.util.CommandDownType;
import gov.nist.javax.sip.header.CallID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.bsj.JedisClusterBSJ;

import javax.sip.Dialog;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Date;
import java.util.Set;


/**
 * @author bsj-chenjunkang
 * @Description:处理INVITE响应
 * @date 2022/8/24
 */
@Component
@Slf4j
public class InviteResponseProcessor implements ISIPResponseProcessor {

    @Autowired
    private CommandUpProducer commandUpProducer;
    @Autowired
    private SIPCommander sipCommander;
    @Autowired
    private VideoStreamSessionManager streamSessionManager;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SipConfig sipConfig;
    @Autowired
    private DeviceChannelMapper deviceChannelMapper;
    @Autowired
    private JedisClusterBSJ jedisClusterBSJ;
    @Autowired
    private CmdLogMapper cmdLogMapper;

    /**
     * 处理invite响应.
     *
     * @param evt 响应消息
     * @throws ParseException
     */
    @Override
    public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException {
        try {
            Response response = evt.getResponse();
            int statusCode = response.getStatusCode();

            CallID callID = (CallID) response.getHeader(CallID.NAME);
            //找到对应的uuid，用于响应mq
            String deviceId = CallIdManager.getDeviceId(callID.getCallId());
//            log.info("收到INVITE响应，状态码是：{}，callId:{}, 获取到的deviceId={}",statusCode, callID.getCallId(), deviceId);
//            String deviceId = "34020000001320000001";
            Device device = deviceMapper.getDeviceByDeviceId(deviceId);
            if (null == device) {
                log.error("收到Invite，查询device为空,{}", deviceId);
                return;
            }
            Long did = device.getId();
            ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
            Command cache = CommandManager.get(deviceId, viaHeader.getBranch());

            if (statusCode == Response.OK) {
                // 成功响应
                // 下发ack
                Dialog dialog = evt.getDialog();
                CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
                Request reqAck = dialog.createAck(cseq.getSeqNumber());

                SipURI requestURI = (SipURI) reqAck.getRequestURI();
                String host = requestURI.getHost();
                int port = requestURI.getPort();
//                log.info("收到Invite时，URI host={}, port={}", host, port);

                if (null == deviceId) {
                    log.error("收到Invite，查询deviceId为空, callId:{}", callID.getCallId());
                    return;
                }
//                log.info("INVITE回复时的设备IP：{}，缓存的cache是：{}", device.getHostAddress(), cache);
                if (cache != null) {
                    if (CommandDownType.DEV_VOD_MEDIA_TRANS != cache.getCmdType()) {
                        // 如果是直播，修改回复的host和port为单个设备的host和port
                        String[] address = device.getHostAddress().split(":");
                        requestURI.setHost(address[0]);
                        requestURI.setPort(Integer.parseInt(address[1]));
//                        log.info("收到INVITE[直播]回复重新修改requestURI的ip={}，host={}", address[0], Integer.parseInt(address[1]));
                    } else {
                        // 如果是回放，找对应的cvr的host和port
                        String nvrId = "";
                        String cvrIds = sipConfig.getCvrIds();
                        String[] split = cvrIds.split(",");
                        String channelId;
                        DeviceChannel deviceChannel = deviceChannelMapper.queryChannelByDeviceIdAndSerial(deviceId, Integer.parseInt(cache.getChannel()));
                        if (deviceChannel == null) {
                            log.error("该设备没有这个通道信息，deviceId={}, channelSerial={}", deviceId, Integer.parseInt(cache.getChannel()));
                            return;
                        } else {
                            channelId = deviceChannel.getChannelId();
//                            log.info("打开{}回放视频时逻辑通道号：{}，真实通道号：{}", nvrId, Integer.parseInt(cache.getChannel()), channelId);
                        }
                        for (String cvrId : split) {
                            Set<String> smembers = jedisClusterBSJ.smembers(RedisConstant.CVR_DEVICE_CHANNELS_KEY + cvrId);
                            if (!CollectionUtils.isEmpty(smembers) && smembers.contains(channelId)) {
                                nvrId = cvrId;
                                break;
                            }
                        }
                        Device deviceNvr = deviceMapper.getDeviceByDeviceId(nvrId);
                        if (deviceNvr != null) {
                            requestURI.setHost(deviceNvr.getIp());
                            requestURI.setPort(deviceNvr.getPort());
//                            log.info("收到INVITE[回放]回复重新修改requestURI的ip={}，host={}", deviceNvr.getIp(), deviceNvr.getPort());
                        }
                    }
                }
                reqAck.setRequestURI(requestURI);
                if (null != cache) {
                    switch (cache.getCmdType()) {
                        //打开实时视频
                        case CommandDownType.REAL_MEDIA_OPEN_CMD:
                        case CommandDownType.REAL_VIDEO_OPEN_CMD:
                            //打开历史视频
                        case CommandDownType.DEV_VOD_MEDIA_TRANS:
                            // 收到设备回复，发送ACK给设备，todo,可以作为指令应答处理
                            doAck(device, cache, dialog, reqAck);
                            break;
                        //打开对讲
                        case CommandDownType.REAL_INTERCOM_OPEN_CMD:
                            doAckAndBroadcast(device, cache, dialog, reqAck);
                            break;
                    }
//                    CommandManager.remove(deviceId, viaHeader.getBranch());
                } else {
                    log.error("处理invite响应，准备发送ACK时，无法找到对应的CommandManager信息，deviceId:{}，branch:{}", deviceId, viaHeader.getBranch());
                }
//                CallIdManager.remove(callID.getCallId());
            }
        } catch (Exception e) {
            log.error("发送实时视音频点播ack错误", e);
        }
    }

    private void doAck(Device device, Command cache, Dialog dialog, Request reqAck) {
//        RocketMqCommand command = new RocketMqCommand(cache.getCmdType(), cache.getUuid(), deviceId, 1);
        try {
            //发送ACK给设备
            dialog.sendAck(reqAck);
//            log.info("Invite发送ACK给设备，指令流程结束，等待视频播放，设备号：{}", device.getDeviceId());
            // 记录指令收发日志
            if (device != null && cache != null) {
                int cmdType = cache.getCmdType();
                String sessionType = cache.getSessionType();

                CmdLog cmdLog = new CmdLog();
                cmdLog.setDeviceId(device.getDeviceId());
                cmdLog.setDeviceName(device.getNickName());
                String desc = null;
                if (cmdType == CommandDownType.REAL_VIDEO_OPEN_CMD) {
                    desc = CmdLogEnum.TYPE_PLAY.getDesc();
                    cmdLog.setType(desc);
                } else if (cmdType == CommandDownType.DEV_VOD_MEDIA_TRANS) {
                    desc = CmdLogEnum.TYPE_PLAY_BACK.getDesc();
                    cmdLog.setType(desc);
                }
                cmdLog.setState(CmdLogEnum.STATE_RECEIVE.getDesc());
                cmdLog.setContent("Invite发送ACK给设备：" + reqAck.toString());
                cmdLog.setTime(new Date());
                cmdLogMapper.insert(cmdLog);

                log.info("Invite发送ACK给设备，指令流程结束，等待{}，设备号：{}", desc, device.getDeviceId());
            }

        } catch (SipException e) {
            //发送ACK失败
//            command.setStatus(0);
//            command.setRetMsg("发送实时视音频点播ack错误");
//            log.error("发送实时视音频点播ack错误，command：{}", JSON.toJSONString(command), e);
        }
//        commandUpProducer.send(command, Constants.SQL_UPDATE);
    }

    private void doAckAndBroadcast(Device device, Command cache, Dialog dialog, Request reqAck) {
        String deviceId = device.getDeviceId();
        try {
            //发送ACK给设备
            dialog.sendAck(reqAck);
            log.info("Invite发送ACK给设备，设备号：{}", deviceId);
            RocketMqCommand command = new RocketMqCommand(cache.getCmdType(), cache.getUuid(), deviceId, 0);
            if (streamSessionManager.contain(deviceId, Constants.BROADCAST_PREFIX, cache.getChannel())) {
                //已有语音广播，响应mq，结束流程
                command.setRetMsg("设备正在对讲中");
            } else {
                //下发语音广播
                sipCommander.broadcastStreamCmd(deviceId, command);
            }
            if (command.getStatus() != 2) {
                //如果不在线或者已有语音广播，才发mq
                commandUpProducer.send(command, Constants.SQL_UPDATE);
            }
        } catch (Exception e) {
            //发送ACK失败
            RocketMqCommand command = new RocketMqCommand(cache.getCmdType(), cache.getUuid(), deviceId, 0);
            command.setRetMsg("发送实时视音频点播ack错误");
            log.error("发送实时视音频点播ack错误，command：{}", JSON.toJSONString(command), e);
            commandUpProducer.send(command, Constants.SQL_UPDATE);
        }
    }


}
